package com.abc.cloud.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.abc.cloud.common.enums.FileCategoryEnums;
import com.abc.cloud.common.enums.FileTypeEnums;
import com.abc.cloud.common.exception.MyException;
import com.abc.cloud.common.utils.*;
import com.abc.cloud.dto.file.FileShareToUserDto;
import com.abc.cloud.dto.user.UserDto;
import com.abc.cloud.entity.FileEntity;
import com.abc.cloud.entity.UserEntity;
import com.abc.cloud.service.FileService;
import com.abc.cloud.service.UserService;
import com.abc.cloud.vo.file.FileShare;
import com.abc.cloud.vo.file.FileShareAdminUpdate;
import com.abc.cloud.vo.file.FileShareCheck;
import com.abc.cloud.vo.file.FileShareUpdate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.abc.cloud.dao.FileShareDao;
import com.abc.cloud.entity.FileShareEntity;
import com.abc.cloud.service.FileShareService;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static com.abc.cloud.common.constants.Constants.GET_SHARE_IP;
import static com.abc.cloud.common.constants.Constants.USER_SHARE_FILE;


@Service("fileShareService")
public class FileShareServiceImpl extends ServiceImpl<FileShareDao, FileShareEntity> implements FileShareService {

    @Autowired
    private FileService fileService;

    @Autowired
    private FileShareService fileShareService;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Value("${webIpaddr}")
    private String webIpaddr;

    @Override
    public PageUtil queryPage(Map<String, Object> params) {
        Long userId = UserHolder.getUser().getId();
        QueryWrapper<FileShareEntity> fileShareEntityQueryWrapper = new QueryWrapper<>();
        fileShareEntityQueryWrapper.eq("user_id", userId);
        fileShareEntityQueryWrapper.isNotNull("uuid");
        fileShareEntityQueryWrapper.and(item -> {
            item.eq("status", 1).or().eq("status", 2);
        });
        IPage<FileShareEntity> page = this.page(
                new Query<FileShareEntity>().getPage(params, "create_time", false),
                fileShareEntityQueryWrapper
        );

        // 检查文件是否失效
        List<FileShareEntity> fileShareEntityList = page.getRecords().stream().map(item -> {
            if (item.getStatus() == 2) return item;
            String code = redisTemplate.opsForValue().get(USER_SHARE_FILE + item.getUuid());
            if (code == null) {
                // 更新数据库
                item.setFilename("分享已过期");
                item.setStatus(2);
                this.baseMapper.updateById(item);
            }
            return item;
        }).collect(Collectors.toList());
        page.setRecords(fileShareEntityList);

        return new PageUtil(page);
    }

    @Override
    public Map<String, String> shareFile(FileShare fileShare) {
        UserEntity userEntity =  UserHolder.getUser();
        Long userId = userEntity.getId();
        String username = userEntity.getUsername();
        FileEntity fileEntity = fileService.getById(fileShare.getFileId());
        if(fileEntity == null){
            throw new MyException("文件不存在");
        }
        if(!fileEntity.getUserId().equals(userId)){
            throw new MyException("身份不正确");
        }

        String uuid = StringUtil.generateRandomString(15);
        String resStr = webIpaddr + "/file/share/" + uuid;

        String code = fileShare.getCode();
        if(code == null || code.isEmpty()){
            // 提取码为空就自动生成一个
            code = StringUtil.generateRandomString(4);
        }

        // 保存数据库
        fileShareService.saveDatabase(fileShare.getTimeRange(), userId, username, fileEntity, uuid, resStr, code);

        // 保存redis并设置过期时间
        redisTemplate.opsForValue().set(USER_SHARE_FILE + uuid, code, fileShare.getTimeRange(), TimeUnit.DAYS);

        Map<String, String> res = new HashMap<>();
        res.put("url", resStr);
        res.put("code", code);
        res.put("timeRange", String.valueOf(fileShare.getTimeRange()));
        return res;
    }

    @Transactional
    public void saveDatabase(Integer timeRange, Long userId, String username, FileEntity fileEntity, String uuid, String resStr, String code) {
        FileShareEntity fileShareEntity = new FileShareEntity();
        fileShareEntity.setCode(code);
        fileShareEntity.setUsername(username);
        fileShareEntity.setUserId(userId);
        fileShareEntity.setFileId(fileEntity.getId());
        fileShareEntity.setUuid(uuid);
        fileShareEntity.setFilename(fileEntity.getFilename());
        fileShareEntity.setFileType(fileEntity.getFileType());
        fileShareEntity.setFolderType(fileEntity.getFolderType());
        fileShareEntity.setTimeRange(timeRange);
        fileShareEntity.setUrl(resStr);
        this.baseMapper.insert(fileShareEntity);

        // 查询是否有子文件
        if (fileEntity.getFolderType() == 2){
            // 将子文件也设置为分享文件
            List<FileEntity> list = this.fileService.list(new QueryWrapper<FileEntity>().eq("p_id", fileEntity.getId()).eq("status", 1));
            for (FileEntity entity : list) {
                // 子文件不设置uuid
                fileShareService.saveDatabase(timeRange, userId, username, entity, null, resStr, code);
            }
        }
    }

    @Override
    public PageUtil getFileShare(Map<String, Object> params, HttpServletRequest httpServletRequest) {
        String uuid = (String) params.get("uuid");
        String pId = (String) params.get("folderId");
        // 查询是否输入提取码
        String ipAddr = IpUtil.getIpAddr(httpServletRequest);
        String flag = redisTemplate.opsForValue().get(GET_SHARE_IP + uuid + ':' + ipAddr);
        if (flag == null){
            throw new MyException("请输入验证码");
        }

        QueryWrapper<FileEntity> fileEntityQueryWrapper = new QueryWrapper<>();
        fileEntityQueryWrapper.eq("status", 1);

        if(pId.equals("0")){
            FileShareEntity fileShareEntity = this.baseMapper.selectOne(new QueryWrapper<FileShareEntity>().eq("uuid", uuid));
            fileEntityQueryWrapper.eq("id", fileShareEntity.getFileId());
        }
        else{
            fileEntityQueryWrapper.eq("p_id", pId);
        }

        IPage<FileEntity> page = fileService.page(
                new Query<FileEntity>().getPage(params, "folder_type", false),
                fileEntityQueryWrapper
        );

        return new PageUtil(page);

    }

    @Override
    public Map<String, Object> info(String uuid, HttpServletRequest httpServletRequest) {
        String trueCode = redisTemplate.opsForValue().get(USER_SHARE_FILE + uuid);
        if(trueCode == null){
            throw new MyException("分享文件不存在或链接已失效");
        }

        FileShareEntity fileShareEntity = this.baseMapper.selectOne(new QueryWrapper<FileShareEntity>().eq("uuid", uuid));
        if(fileShareEntity == null){
            throw new MyException("分享文件不存在");
        }

        UserEntity userEntity = userService.getOne(new QueryWrapper<UserEntity>().eq("id", fileShareEntity.getUserId()));
        if(userEntity == null){
            throw new MyException("用户不存在");
        }

        Map<String, Object> res = new HashMap<>();

        // 查询文件是否已经输入了提取码
        String ipAddr = IpUtil.getIpAddr(httpServletRequest);
        String str = redisTemplate.opsForValue().get(GET_SHARE_IP + uuid + ':'+ ipAddr);
        if (str != null) res.put("flag", true);
        else res.put("flag", false);

        res.put("userInfo", BeanUtil.copyProperties(userEntity, UserDto.class));
        res.put("fileInfo", BeanUtil.copyProperties(fileShareEntity, FileShareToUserDto.class));

        return res;
    }

    @Transactional
    @Override
    public Boolean check(FileShareCheck fileShareCheck,
                         HttpServletRequest httpServletRequest) {
        String trueCode = redisTemplate.opsForValue().get(USER_SHARE_FILE + fileShareCheck.getUuid());
        if (trueCode == null){
            throw new MyException("分享文件不存在或链接已失效");
        }
        if(!trueCode.equals(fileShareCheck.getCode())){
            return false;
        }
        // 保存IP
        String ipAddr = IpUtil.getIpAddr(httpServletRequest);
        redisTemplate.opsForValue().set(GET_SHARE_IP + fileShareCheck.getUuid() + ':'+ ipAddr, trueCode, 1, TimeUnit.HOURS);

        return true;
    }

    @Override
    public void updateShare(FileShareUpdate fileShareUpdate) {
        Long userId = UserHolder.getUser().getId();
        FileShareEntity fileShareEntity = this.baseMapper.selectById(fileShareUpdate.getId());
        if (!fileShareEntity.getUserId().equals(userId)){
            throw new MyException("身份验证出错");
        }

        BeanUtils.copyProperties(fileShareUpdate, fileShareEntity);

        this.baseMapper.updateById(fileShareEntity);
        redisTemplate.delete(USER_SHARE_FILE + fileShareEntity.getUuid());
    }

    @Override
    public void download(HttpServletResponse response, String uuid, Long id, HttpServletRequest request, Long userId) {
        // 检查是否有资格下载文件
        String ipAddr = IpUtil.getIpAddr(request);
        String str = redisTemplate.opsForValue().get(GET_SHARE_IP + uuid + ':'+ ipAddr);
        if (str == null){
           throw new MyException("权限不足");
        }

        System.out.println(str);
        // 检查文件是否属于分享文件
        FileShareEntity fileShareEntity = this.baseMapper.selectOne(new QueryWrapper<FileShareEntity>().eq("file_id", id).eq("status", 1)
        .eq("code", str));
        if (fileShareEntity == null){
            throw new MyException("无法下载未分享的文件或过期文件");
        }

        FileEntity fileEntity = fileService.getById(id);
        fileService.getFileStream(response, fileEntity.getFilename(), fileEntity.getFilePath());
//        fileService.getFile(response, request, id, userId);

    }

    @Override
    public PageUtil adminQueryList(Map<String, Object> params) {
        QueryWrapper<FileShareEntity> queryWrapper = new QueryWrapper<>();

        String key = (String) params.get("key");
        if (key != null){
            queryWrapper.and(item -> {
                item.like("filename", key).or().like("username", key);
            });
        }

        IPage<FileShareEntity> page = this.page(
                new Query<FileShareEntity>().getPage(params, "create_time", false),
                queryWrapper
        );

        return new PageUtil(page);
    }

    @Override
    public void adminDeleteFile(List<Long> fileIds, Integer status) {
        this.baseMapper.deleteFileByIds(fileIds, status);
    }

    @Transactional
    @Override
    public void adminUpdateShareFile(FileShareAdminUpdate fileShareAdminUpdate) {
        FileShareEntity fileShareEntity = this.baseMapper.selectById(fileShareAdminUpdate.getId());
        BeanUtils.copyProperties(fileShareAdminUpdate, fileShareEntity);

        // 如果是文件夹就去掉文件类型
        if(fileShareEntity.getFolderType() == 2){
            fileShareEntity.setFileType(null);
        }

        // 处理有效期
        if (fileShareEntity.getStatus() == 2 || fileShareEntity.getStatus() == 3){
            // 如果是失效或删除，就删除redis中的数据
            redisTemplate.delete(USER_SHARE_FILE + fileShareEntity.getUuid());
            // 更改文件名
            fileShareEntity.setFilename("分享已过期");
        }
        else{
            // 重置redis过期时间
            redisTemplate.opsForValue().set(USER_SHARE_FILE + fileShareEntity.getUuid(), fileShareEntity.getCode(),
                    fileShareEntity.getTimeRange(), TimeUnit.DAYS);
        }

        this.baseMapper.updateById(fileShareEntity);
    }
}